home *** CD-ROM | disk | FTP | other *** search
Wrap
#define STRICT // Includes standard Windows #include <windows.h> #include <windowsx.h> #include <time.h> #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <stdio.h> // Includes D3D #define D3D_OVERLOADS #include <ddraw.h> #include <d3d.h> // Includes utilitaires D3D #include "d3dmath.h" #include "d3dutil.h" #include "D3DEnum.h" #include <d3dx.h> // Ids Resources //#include "resource.h" // Constantes #include "const.h" // Types #include "types.h" // Variables globales projet #include "vars.h" // Prototypes fonctions autres modules #include "proto.h" // Macros #include "macros.h" #define XDC_CLIPPINGDIST (4.f) static W3D_Context *hW3DC_3D; // Contexte Warp3D associé à la fenêtre 3D static HBITMAP hbDBuf_3D; // Bitmap offscreen du double buffer static DC dcDBuf_3D, *hdcDBuf_3D = &dcDBuf_3D; // RastPort du double buffer static bZBuf = FALSE; // TRUE si on est arrivé à allouer le ZBuf dans le contexte W3D, FALSE sinon static int iWidth, iHeight; // Mémoire largeur / hauteur pour ne rien faire si NEWSIZE avec mêmes dimensions static long lError; static BOOL bImage; // TRUE si une image est OK dans le double buffer, FALSE sinon static W3D_Triangle sTriangle; static W3D_Double dZdepth = 1.; static W3D_Float fLeftEdge, fTopEdge, fW, fH, fXMed, fYMed; static int iLeftEdge, iTopEdge, iW, iH; static W3D_Vertex sV[4]; static W3D_Triangles sTs; static W3D_Color sC = {0., 0., 0., 1.}; static W3D_Lines sLs; static W3D_Point sP; static inline void vShow3DFrame(void) { if (!bImage) return; ClipBlit(hdcDBuf_3D, iLeftEdge, iTopEdge, hWndPersp -> RPort, iLeftEdge, iTopEdge, iW, iH, 0xc0); } // Versions plus rapides que celle de D3Dmath static inline void vFastVectorMatrixMultiply(D3DVECTOR *vDest, D3DVECTOR *vSrc, D3DMATRIX *mat) // !!! vDest != vSrc !!! { FLOAT w = vSrc -> x * mat -> _m._14 + vSrc -> y * mat -> _m._24 + vSrc -> z * mat -> _m._34 + mat -> _m._44; vDest -> x = (vSrc -> x * mat -> _m._11 + vSrc -> y * mat -> _m._21 + vSrc -> z * mat -> _m._31 + mat -> _m._41) / w; vDest -> y = (vSrc -> x * mat -> _m._12 + vSrc -> y * mat -> _m._22 + vSrc -> z * mat -> _m._32 + mat -> _m._42) / w; vDest -> z = (vSrc -> x * mat -> _m._13 + vSrc -> y * mat -> _m._23 + vSrc -> z * mat -> _m._33 + mat -> _m._43) / w; } static inline void vFastNormalize(register D3DVECTOR *vDest, register D3DVECTOR *vSrc) { register FLOAT fNorm = sqrt(vSrc -> x * vSrc -> x + vSrc -> y * vSrc -> y + vSrc -> z * vSrc -> z); vDest -> x = vSrc -> x / fNorm; vDest -> y = vSrc -> y / fNorm; vDest -> z = vSrc -> z / fNorm; } static void vRender3DEnvironment(void) { bImage = FALSE; // Verrouiller le hardware 3D if (!hW3DC_3D || !hbDBuf_3D || W3D_SUCCESS != (lError = W3D_LockHardware(hW3DC_3D))) { vTrace("*** E0067 : vue 3D : lock hardware (contexte @%08lX, code %ld, bm %08lX)", hW3DC_3D, lError, hbDBuf_3D); return; } // Clearer le zbuf if (dZBuf == D3DZB_TRUE && bZBuf) W3D_ClearZBuffer(hW3DC_3D, &dZdepth); // Set Drawing region sScissor.left = (int) fLeftEdge; sScissor.top = (int) fTopEdge; sScissor.width = (int) fW; sScissor.height = (int) fH; W3D_SetDrawRegion(hW3DC_3D, NULL, 0, &sScissor); // Calculer le produit des matrices view / proj D3DMath_MatrixMultiply(matWorld, matView, matProj); // Gouraud shading & zbuffer off W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_DISABLE); // Remplir le fond WSetPen(hW3DC_3D, cBack); WRectFill(hW3DC_3D, 0, 0, fW - 1, fH - 1); // Gouraud shading & zBuffer on W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_ENABLE); if (dZBuf == D3DZB_TRUE && bZBuf) W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_ENABLE); // Calculer le vecteur normalisé observateur / cible (pour le lighting simpliste) D3DVECTOR vObs = Normalize(Observer - Target); // Dessiner les triangles for (register int iTriangle = 0 ; iTriangle <= iTriaLastUsed ; iTriangle++) { // Déréférencer le triangle register gTri *hTri = &Triangles[iTriangle]; // Si le triangle n'est pas enabled ou est caché alors ne rien faire if (!hTri -> bEnabled) continue; if (hTri -> bHidden) continue; D3DVECTOR // Déréférencer les 3 sommets du triangle v0_UnT = Vertices[hTri -> iSommets[0]].vPoint, v1_UnT = Vertices[hTri -> iSommets[1]].vPoint, v2_UnT = Vertices[hTri -> iSommets[2]].vPoint, // Transformés en coordonnées homogènes des 3 vecteurs ci-dessus v0, v1, v2; // Sommets copiés pour dessiner l'outline du triangle s'il est en cours de texturage W3D_Vertex vl0, vl1, vl2; // Déréférencer le material du triangle register gMtrl *hMat = &(Materials[hTri -> iMtrl]); D3DMATERIAL7 *hMtrl = &(hMat -> mtrl); // CLIPPING, step 1 : éliminer les triangles dont au moins un des sommets est trop proche de la caméra if ( SquareMagnitude(v0_UnT - Observer) < XDC_CLIPPINGDIST || SquareMagnitude(v1_UnT - Observer) < XDC_CLIPPINGDIST || SquareMagnitude(v2_UnT - Observer) < XDC_CLIPPINGDIST ) continue; // TRANSFORM, step 1 : Transformation coordonnées 3D vers [-0.5..+0.5] en utilisant la concaténation des matrices View et Proj vFastVectorMatrixMultiply(&v0, &v0_UnT, &matWorld); vFastVectorMatrixMultiply(&v1, &v1_UnT, &matWorld); vFastVectorMatrixMultiply(&v2, &v2_UnT, &matWorld); // CLIPPING, step 2 : élimination des triangles en dehors du range de zbuf qu'on veut dessiner if ( v0.z < 0.f || v0.z > 1.f || v1.z < 0.f || v1.z > 1.f || v2.z < 0.f || v2.z > 1.f ) continue; // CLIPPING, step 3 : élimination des triangles complètement hors champ // (le reste est laissé à Warp3D, ce qui n'est pas forcément génial) if ( (v0.x < -0.5f && v1.x < -0.5f && v2.x < -0.5f) // complètement à gauche || (v0.x > 0.5f && v1.x > 0.5f && v2.x > 0.5f) // complètement à droite || (v0.y < -0.5f && v1.y < -0.5f && v2.y < -0.5f) // complètement en haut || (v0.y > 0.5f && v1.y > 0.5f && v2.y > 0.5f) // complètement en bas ) continue; // TRANSFORM, step 2 : ajustement du span X/Y dans fenêtre 3D sTriangle.v1.x = fXMed + v0.x * fW; sTriangle.v1.y = fYMed - v0.y * fH; sTriangle.v2.x = fXMed + v1.x * fW; sTriangle.v2.y = fYMed - v1.y * fH; sTriangle.v3.x = fXMed + v2.x * fW; sTriangle.v3.y = fYMed - v2.y * fH; sTriangle.v1.z = v0.z; sTriangle.v2.z = v1.z; sTriangle.v3.z = v2.z; // LIGHT, step 1 : calculer les vecteurs normaux sur les trois sommets D3DVECTOR v01 = v1_UnT - v0_UnT, v02 = v2_UnT - v0_UnT, v12 = v2_UnT - v1_UnT, vn0, vn1, vn2; vn0 = CrossProduct(v01, v02), vn1 = CrossProduct(v01, v12), vn2 = CrossProduct(v02, v12); vFastNormalize(&vn0, &vn0), vFastNormalize(&vn1, &vn1), vFastNormalize(&vn2, &vn2); // Déréférencer les 3 couleurs à calculer W3D_Color *c1 = &sTriangle.v1.color, *c2 = &sTriangle.v2.color, *c3 = &sTriangle.v3.color; // Pour l'Alpha, seule la diffuse est prise en compte, et c'est la même pour les 3 sommets c1 -> a = c2 -> a = c3 -> a = hMtrl -> diffuse.a; // On a 2 modes de lighting : // A - simpliste : 1 seule lampe, à la place de la caméra ; produit avec la seule composante diffuse du matériau if (!bLightFull) { // LIGHT, step 2a : Calculer les facteurs de pondération pour les 3 sommets : cos(angle vecteur regard / normale) FLOAT f0 = fabs(DotProduct(vObs, vn0)), f1 = fabs(DotProduct(vObs, vn1)), f2 = fabs(DotProduct(vObs, vn2)); // LIGHT, step 3a : moduler la couleur en fonction de la pondération calculée FLOAT r = hMtrl -> diffuse.r, g = hMtrl -> diffuse.g, b = hMtrl -> diffuse.b; c1 -> r = r * f0; c1 -> g = g * f0; c1 -> b = b * f0; c2 -> r = r * f1; c2 -> g = g * f1; c2 -> b = b * f1; c3 -> r = r * f2; c3 -> g = g * f2; c3 -> b = b * f2; } else // B - complet : prise en compte de toutes les lampes et de toutes les composantes lampe / material { // LIGHT, step 2b : pré accumuler dans les RGB des 3 sommets la somme (material emissive + material ambient * ambient light) FLOAT r0 = hMtrl -> emissive.r + hMtrl -> ambient.r * WL_RED(cAmbient), r1 = r0, r2 = r0, g0 = hMtrl -> emissive.g + hMtrl -> ambient.g * WL_GREEN(cAmbient), g1 = g0, g2 = g0, b0 = hMtrl -> emissive.b + hMtrl -> ambient.b * WL_BLUE(cAmbient), b1 = b0, b2 = b0; // LIGHT, step 3b : ajouter la contribution de chaque lampe ... for (int iLamp = 0 ; iLamp <= iLampLastUsed ; iLamp++) { // Déférérencer la lampe D3DLIGHT7 *hLamp = &(Lampes[iLamp].lLamp); // Si la lampe n'est pas enabled ni allumée, ne rien faire if (!Lampes[iLamp].bEnabled) continue; if (!Lampes[iLamp].bLit) continue; FLOAT // LIGHTING, step 4b : calculer les 3 pondérations : cos(lampe/sommet, normale au sommet) f0 = fabs(DotProduct(Normalize(v0_UnT - hLamp -> dvPosition), vn0)), f1 = fabs(DotProduct(Normalize(v1_UnT - hLamp -> dvPosition), vn1)), f2 = fabs(DotProduct(Normalize(v2_UnT - hLamp -> dvPosition), vn2)), // LIGHTING, step 5b : calculer les 3 distances lampe / sommet d0 = Magnitude(v0_UnT - hLamp -> dvPosition), d1 = Magnitude(v1_UnT - hLamp -> dvPosition), d2 = Magnitude(v2_UnT - hLamp -> dvPosition), // LIGHTING, step 6b : calculer les 3 facteurs d'atténuation polynomiale (0, 1, 2) a0 = 1. / (hLamp -> dvAttenuation0 + d0 * hLamp -> dvAttenuation1 + d0 * d0 * hLamp -> dvAttenuation2), a1 = 1. / (hLamp -> dvAttenuation0 + d1 * hLamp -> dvAttenuation1 + d1 * d1 * hLamp -> dvAttenuation2), a2 = 1. / (hLamp -> dvAttenuation0 + d2 * hLamp -> dvAttenuation1 + d2 * d2 * hLamp -> dvAttenuation2), // LIGHTING, step 7b : calculer les contributions RGB diffuse de la lampe (lampe * material) non atténuées ldr = hMtrl -> diffuse.r * hLamp -> dcvDiffuse.r, ldg = hMtrl -> diffuse.g * hLamp -> dcvDiffuse.g, ldb = hMtrl -> diffuse.b * hLamp -> dcvDiffuse.b, // LIGHTING, step 8b : calculer les contributions RGB ambient de la lampe (lampe * material) non atténuées lar = hMtrl -> ambient.r * hLamp -> dcvAmbient.r, lag = hMtrl -> ambient.g * hLamp -> dcvAmbient.g, lab = hMtrl -> ambient.b * hLamp -> dcvAmbient.b; // LIGHTING, steb 9b : accumuler les contributions diffuse et ambient r0 += (ldr * f0 + lar) * a0; r1 += (ldr * f1 + lar) * a1; r2 += (ldr * f2 + lar) * a2; g0 += (ldg * f0 + lag) * a0; g1 += (ldg * f1 + lag) * a1; g2 += (ldg * f2 + lag) * a2; b0 += (ldb * f0 + lab) * a0; b1 += (ldb * f1 + lab) * a1; b2 += (ldb * f2 + lab) * a2; } // LIGHTING, step 10b : caper les couleurs avec 1. (devrait être 1 mais sur Permedia 2 on retombe à noir au dessus de 0.998) c1 -> r = min(0.998f, r0); c1 -> g = min(0.998f, g0); c1 -> b = min(0.998f, b0); c2 -> r = min(0.998f, r1); c2 -> g = min(0.998f, g1); c2 -> b = min(0.998f, b1); c3 -> r = min(0.998f, r2); c3 -> g = min(0.998f, g2); c3 -> b = min(0.998f, b2); } // Si le triangle est celui en cours de texturage, mémoriser les sommets pour en dessiner ensuite le contour (après avoir dessiné le triangle) if (iTriangle == iTriaHilit) { vl0 = sTriangle.v1; vl1 = sTriangle.v2; vl2 = sTriangle.v3; } // Si le matériau ext texturé et que la texture est valide, régler U/V et mettre le texmapping ON if (hMat -> bTextured && (sTriangle.tex = Textures[hMat -> iTexture].hTexture)) { // Set Texture mapping on W3D_SetState(hW3DC_3D, W3D_TEXMAPPING, W3D_ENABLE); // Affecter les u & v sTriangle.v1.u = hTri -> u[0]; sTriangle.v1.v = hTri -> v[0]; sTriangle.v2.u = hTri -> u[1]; sTriangle.v2.v = hTri -> v[1]; sTriangle.v3.u = hTri -> u[2]; sTriangle.v3.v = hTri -> v[2]; // Calculer les w pour la correction de perspective sTriangle.v1.w = 1. - v0.z; sTriangle.v2.w = 1. - v1.z; sTriangle.v3.w = 1. - v2.z; } switch(dFillMode) { case D3DFILL_SOLID : // Facettes (gouraud ou texture) W3D_DrawTriangle(hW3DC_3D, &sTriangle); break; case D3DFILL_WIREFRAME : // Fil de fer sLs.tex = sTriangle.tex; sV[0] = sV[3] = sTriangle.v1; sV[1] = sTriangle.v2; sV[2] = sTriangle.v3; W3D_DrawLineStrip(hW3DC_3D, &sLs); break; case D3DFILL_POINT : // Points sP.tex = sTriangle.tex; sP.v1 = sTriangle.v1; W3D_DrawPoint(hW3DC_3D, &sP); sP.v1 = sTriangle.v2; W3D_DrawPoint(hW3DC_3D, &sP); sP.v1 = sTriangle.v3; W3D_DrawPoint(hW3DC_3D, &sP); break; } // Set Texture mapping off if (hMat -> bTextured && sTriangle.tex) W3D_SetState(hW3DC_3D, W3D_TEXMAPPING, W3D_DISABLE); // Si le triangle est celui en cours d'édition, dessiner un contour jaune if (iTriangle == iTriaHilit) { // Arrêter le zbuf et le gouraud W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_DISABLE); // Définir le jaune comme couleur flat WSetPen(hW3DC_3D, XDC_COL_GREEN); // Dessiner le contour du triangle sV[0] = sV[3] = vl0; sV[1] = vl1; sV[2] = vl2; W3D_DrawLineStrip(hW3DC_3D, &sLs); // Remettre le gouraud et le zbuf W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_ENABLE); if (dZBuf == D3DZB_TRUE && bZBuf) W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_ENABLE); } } // Déverrouiller le hardware 3D W3D_UnLockHardware(hW3DC_3D); // Mémoriser qu'on a bien créé une image dans le double buffer bImage = TRUE; // Blitter le double dans la fenêtre vShow3DFrame(); } inline void vForce3DRefresh(BOOL bFull) { if (!bActive) return; if (bFull == XDC_MODE_COMPLET) vRender3DEnvironment(); else vShow3DFrame(); } void vSetHints(void) { W3D_Hint(hW3DC_3D, W3D_H_TEXMAPPING, uHint[0]); W3D_Hint(hW3DC_3D, W3D_H_MIPMAPPING, uHint[1]); W3D_Hint(hW3DC_3D, W3D_H_BILINEARFILTER, uHint[2]); W3D_Hint(hW3DC_3D, W3D_H_MMFILTER, uHint[3]); W3D_Hint(hW3DC_3D, W3D_H_PERSPECTIVE, uHint[4]); W3D_Hint(hW3DC_3D, W3D_H_BLENDING, uHint[5]); W3D_Hint(hW3DC_3D, W3D_H_FOGGING, uHint[6]); W3D_Hint(hW3DC_3D, W3D_H_ANTIALIASING, uHint[7]); W3D_Hint(hW3DC_3D, W3D_H_DITHERING, uHint[8]); W3D_Hint(hW3DC_3D, W3D_H_ZBUFFER, uHint[9]); } LRESULT CALLBACK lrPerspWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_PAINT: // If we get WM_PAINT messages, it usually means our window was // covered up, so we need to refresh it by re-showing the contents // of the current frame. vForce3DRefresh(XDC_MODE_PARTIEL); break; case WM_CREATE: InitRastPort(hdcDBuf_3D); sTs.vertexcount = 4; sTs.v = sV; sLs.vertexcount = 4; sLs.v = sV; sLs.st_enable = FALSE; sLs.linewidth = 3.; sP.tex = NULL; sP.pointsize = 3.; case WM_SIZE: vTrace("### Vue 3D : allocation double buffer / contexte Warp3D / z-buffer..."); { bReady = FALSE; // Si la taille n'a pas changé, ne rien faire if (iWidth == hWnd -> Width && iHeight == hWnd -> Height) break; iWidth = hWnd -> Width; iHeight = hWnd -> Height; iLeftEdge = (int) (fLeftEdge = hWndPersp -> BorderLeft); iTopEdge = (int) (fTopEdge = hWndPersp -> BorderTop); iW = (int) (fW = hWndPersp -> Width - fLeftEdge - hWndPersp -> BorderRight); iH = (int) (fH = hWndPersp -> Height - fTopEdge - hWndPersp -> BorderBottom); fXMed = fLeftEdge + fW / 2.; fYMed = fTopEdge + fH / 2.; // Supprimer le contexte 3D s'il est déjà alloué if (hW3DC_3D) { if (bZBuf) { W3D_FreeZBuffer(hW3DC_3D); bZBuf = FALSE; } vCloseTextures(hW3DC_3D); W3D_DestroyContext(hW3DC_3D); hW3DC_3D = NULL; } // Supprimer le bitmap offscreen s'il est déjà alloué if (hbDBuf_3D) FreeBitMap(hbDBuf_3D); // Créer le bitmap offscreen if (!(hbDBuf_3D = AllocBitMap(hWnd -> Width, hWnd -> Height, 8, BMF_MINPLANES | BMF_DISPLAYABLE, hWnd -> RPort -> BitMap))) { vTrace("*** E0068 : vue 3D : allocation double buffer"); break; } // Attacher le bitmap au rastport dcDBuf_3D.BitMap = hbDBuf_3D; // Créer le contexte W3D extern ULONG lModeID, lError; if (!(hW3DC_3D = W3D_CreateContextTags(&lError, W3D_CC_MODEID, lModeID, W3D_CC_BITMAP, (ULONG) hbDBuf_3D, W3D_CC_YOFFSET, 0, W3D_CC_DRIVERTYPE, W3D_DRIVER_BEST, W3D_CC_FAST, TRUE, TAG_DONE))) { vTrace("*** E0069 : vue 3D : crétion contexte Warp3D (code %ld)", lError); FreeBitMap(hbDBuf_3D); hbDBuf_3D = NULL; break; } // Charger les textures GenTextures(hW3DC_3D); // Set alpha blending mode // W3D_SetBlendMode(hW3DC_3D, W3D_SRC_ALPHA, W3D_ONE_MINUS_SRC_ALPHA); // Hints vSetHints(); // Set states pour le contexte 3D W3D_SetState(hW3DC_3D, W3D_DITHERING, W3D_ENABLE); W3D_SetState(hW3DC_3D, W3D_SCISSOR, W3D_ENABLE); W3D_SetState(hW3DC_3D, W3D_PERSPECTIVE, W3D_ENABLE); W3D_SetState(hW3DC_3D, W3D_AUTOTEXMANAGEMENT, W3D_ENABLE); W3D_SetState(hW3DC_3D, W3D_BLENDING, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_SYNCHRON, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_INDIRECT, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_TEXMAPPING, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_FOGGING, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_LOGICOP, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_STENCILBUFFER, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_DOUBLEHEIGHT, W3D_DISABLE); // Les 2 suivants n'ont pas besoin d'être réglés ici : gérés dans Render3D...() #if 0 W3D_SetState(hW3DC_3D, W3D_GOURAUD, W3D_DISABLE); W3D_SetState(hW3DC_3D, W3D_ZBUFFER, W3D_DISABLE); #endif // Allouer le ZBuffer if (W3D_AllocZBuffer(hW3DC_3D)) { W3D_SetState(hW3DC_3D, W3D_ZBUFFERUPDATE, W3D_DISABLE); vTrace("*** E0047 : vue 3D : allocation ZBuffer -> pas de suppression faces cachées"); } else { W3D_SetZCompareMode(hW3DC_3D, W3D_Z_LESS); W3D_SetState(hW3DC_3D, W3D_ZBUFFERUPDATE, W3D_ENABLE); bZBuf = TRUE; vTrace("### Vue 3D : allocation double buffer / contexte Warp3D / z-buffer OK"); } // Redessiner la 3D dans le double buffer, Intuition va de de toute façon nous envoyer un REFRESH qui nous donnera un PAINT qui nous fera blitter le double buffer dans la fenêtre vRender3DEnvironment(); bReady = TRUE; } break; case WM_COMMAND: // Rediriger les frappes clavier et chois menus vers la fenêtre menus case WM_CHAR : PostMessage(hWndMenu, uMsg, wParam, lParam ); break; case WM_DESTROY: // Supprimer le contexte 3D if (hW3DC_3D) { if (bZBuf) { W3D_FreeZBuffer(hW3DC_3D); bZBuf = FALSE; } vCloseTextures(hW3DC_3D); W3D_DestroyContext(hW3DC_3D); hW3DC_3D = NULL; } // Supprimer le bitmap offscreen du double buffer if (hbDBuf_3D) FreeBitMap(hbDBuf_3D); PostQuitMessage(0); return 0L; } #ifndef _AMIGA_ return DefWindowProc( hWnd, uMsg, wParam, lParam ); #else return 0; #endif }